#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include "jpeglib.h"
#include "jpeg.h"

extern int *lcd_ptr;

// 错误对象收集结构体
struct my_error_mgr
{
	struct jpeg_error_mgr pub; /* "public" fields */
	jmp_buf setjmp_buffer; /* for return to caller */
};

typedef struct my_error_mgr *my_error_ptr;

/*
 * Here's the routine that will replace the standard error_exit method:
 */
METHODDEF(void)
my_error_exit(j_common_ptr cinfo)
{
	/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
	my_error_ptr myerr = (my_error_ptr)cinfo->err;

	/* Always display the message. */
	/* We could postpone this until after returning, if we chose. */
	(*cinfo->err->output_message)(cinfo);

	/* Return control to the setjmp point */
	longjmp(myerr->setjmp_buffer, 1);
}

GLOBAL(int)
read_JPEG_file(const char *filename, int *image_width, int *image_height, char **out_buffer)
{
	/* This struct contains the JPEG decompression parameters and pointers to
	 * working space (which is allocated as needed by the JPEG library).
	 */
	struct jpeg_decompress_struct cinfo;
	/* We use our private extension JPEG error handler.
	 * Note that this struct must live as long as the main JPEG parameter
	 * struct, to avoid dangling-pointer problems.
	 */
	struct my_error_mgr jerr;
	/* More stuff */
	FILE *infile;	   /* source file */
	JSAMPARRAY buffer; /* Output row buffer */
	int row_stride;	   /* physical row width in output buffer */

	/* In this example we want to open the input file before doing anything else,
	 * so that the setjmp() error recovery below can assume the file is open.
	 * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
	 * requires it in order to read binary files.
	 */

	if ((infile = fopen(filename, "rb")) == NULL)
	{
		fprintf(stderr, "can't open %s\n", filename);
		return 0;
	}

	/* Step 1: 分配并初始化解压缩对象 */

	/* We set up the normal JPEG error routines, then override error_exit. */
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	/* Establish the setjmp return context for my_error_exit to use. */
	if (setjmp(jerr.setjmp_buffer))
	{
		/* If we get here, the JPEG code has signaled an error.
		 * We need to clean up the JPEG object, close the input file, and return.
		 */
		jpeg_destroy_decompress(&cinfo);
		fclose(infile);
		return 0;
	}
	/* Now we can initialize the JPEG decompression object. */
	jpeg_create_decompress(&cinfo);

	/* Step 2: 指定解压缩数据源 */

	jpeg_stdio_src(&cinfo, infile);

	/* Step 3: 读取文件头数据 */

	(void)jpeg_read_header(&cinfo, TRUE);
	/* We can ignore the return value from jpeg_read_header since
	 *   (a) suspension is not possible with the stdio data source, and
	 *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
	 * See libjpeg.txt for more info.
	 */

	*image_width = cinfo.image_width;
	*image_height = cinfo.image_height;

	/* Step 4: 设置解压参数，可以直接使用默认的解压缩参数 */

	/* In this example, we don't need to change any of the defaults set by
	 * jpeg_read_header(), so we do nothing here.
	 */

	// unsigned int scale_num, scale_denom; /* 设定解压缩参数比例 */
	// cinfo.scale_num = 1; cinfo.scale_denom = 1; // 原尺寸
	// cinfo.scale_num = 2; cinfo.scale_denom = 1; // 1/2尺寸

	/* Step 5: 开始解压 */

	(void)jpeg_start_decompress(&cinfo);
	/* We can ignore the return value since suspension is not possible
	 * with the stdio data source.
	 */

	/* We may need to do some setup of our own at this point before reading
	 * the data.  After jpeg_start_decompress() we have the correct scaled
	 * output image dimensions available, as well as the output colormap
	 * if we asked for color quantization.
	 * In this example, we need to make an output work buffer of the right size.
	 */
	/* 一行的数据大小 */
	row_stride = cinfo.output_width * cinfo.output_components;

	*out_buffer = calloc(1, row_stride * cinfo.image_height);

	/* 一行数据的存储缓冲区 */
	buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);

	char *dst = *out_buffer;

	/* Step 6: 循环读取每一行数据 */

	/* Here we use the library's state variable cinfo.output_scanline as the
	 * loop counter, so that we don't have to keep track ourselves.
	 */
	while (cinfo.output_scanline < cinfo.output_height)
	{
		/* jpeg_read_scanlines expects an array of pointers to scanlines.
		 * Here the array is only one element long, but you could ask for
		 * more than one scanline at a time if that's more convenient.
		 */
		(void)jpeg_read_scanlines(&cinfo, buffer, 1);

		memcpy(dst, buffer[0], row_stride);
		dst += row_stride;
	}

	/* Step 7: 解压完成 */

	(void)jpeg_finish_decompress(&cinfo);
	/* We can ignore the return value since suspension is not possible
	 * with the stdio data source.
	 */

	/* Step 8: 释放资源 */

	/* This is an important step since it will release a good deal of memory. */
	jpeg_destroy_decompress(&cinfo);

	/* After finish_decompress, we can close the input file.
	 * Here we postpone it until after no more JPEG errors are possible,
	 * so as to simplify the setjmp error logic above.  (Actually, I don't
	 * think that jpeg_destroy can do an error exit, but why assume anything...)
	 */
	fclose(infile);

	/* At this point you may want to check to see whether any corrupt-data
	 * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
	 */

	/* And we're done! */
	return 1;
}

static int width, height;

static void jpeg_to_lcd(int x, int y, char *jpeg_buffer)
{
	int i, j;
	int b, g, r, color;

	for (j = 0; j < height; j++)
	{
		for (i = 0; i < width; i++)
		{
			r = jpeg_buffer[(width * j + i) * 3 + 0];
			g = jpeg_buffer[(width * j + i) * 3 + 1];
			b = jpeg_buffer[(width * j + i) * 3 + 2];
			color = r << 16 | g << 8 | b;
			lcd_ptr[800 * (j + y) + i + x] = color;
		}
	}
}

int lcd_draw_jpeg(int x, int y, const char *pathname)
{
	char *jpeg_buffer = NULL;

	// 1. 解压
	if (!read_JPEG_file(pathname, &width, &height, &jpeg_buffer)) {
		return -1;
	}
	
	// 2. 显示
	jpeg_to_lcd(x, y, jpeg_buffer);
	
	// 3. 释放内存
	free(jpeg_buffer);
	
	return 0;
} 